<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript">
<!--

var TagHeader = function() {
  //
  // public members
  //
  this.tag = 0
  this.type = 0
  this.size = 0
  this.nextPosition = 0
}

var IdfData = function() {
  this.stripOffset = -1
  this.compression = -1
  this.bps = undefined
  this.samples = -1
  this.imageSize = -1
  this.width = -1
  this.height = -1
}

var RawDataDecoder = function(buf, idf) {
  var _buf = buf
  var _idf = idf
  var _currIdx = 0
  var _currBitIdx = 0
  var _rawBuf = undefined
  var _rgbBuf = undefined

  //
  // private methods
  //
  function _getMaskedAvailByte() {
    var BITMASK = [0xff, 0x7f, 0x3f, 0x1f, 0x0f, 0x07, 0x03, 0x01]
    var mask = BITMASK[_currBitIdx]
    return _buf[_currIdx] & mask
  }

  function _incStreamBits(bits) {
    _currBitIdx += bits
    while (_currBitIdx >= 8) {
      _currBitIdx -= 8
      _currIdx++
    }
  }

  function _getOneValue() {
    var val = 0
    var remainBits = _idf.bps
    while (remainBits > 0) {
      var availBits = 8 - _currBitIdx
      var consumedBits = 0
      var v = 0
      if (availBits >= remainBits) {
          consumedBits = remainBits
          v = _getMaskedAvailByte()
          v = v >> (8 - _currBitIdx - consumedBits) 
      } else {
          consumedBits = availBits
          v = _getMaskedAvailByte()
      }
      val = (val << consumedBits)
      val += v

      _incStreamBits(consumedBits)
      remainBits -= consumedBits
    }
    return val
  }

  function makeRawBuf() {
    var size = _idf.width * _idf.height
    _rawBuf = new Uint16Array(size)

    for (var y = 0; y < _idf.height; y++) {
      for (var x = 0; x < _idf.width; x++) {
        var idx = y * _idf.width + x
        _rawBuf[idx] = _getOneValue()
      }
    }
  }

  function _rawDataAt(x, y) {
    if (!_rawBuf)
      makeRawBuf()
    var idx = y * _idf.width + x
    return _rawBuf[idx]
  }

  function _calcRgbBufIndex(x, y) {
    return (y * _idf.width + x) * 3
  }

  function _calcAverageColor(x, y, colorBufOfs, refPositions) {
    var sum = 0
    var num = 0
    for (var i = 0; i < refPositions.length; i++) {
      var refX = x + refPositions[i][0]
      var refY = y + refPositions[i][1]
      if (refX < 0 || refX >= idf.width)
        continue
      if (refY < 0 || refY >= idf.height)
        continue

      var idx = _calcRgbBufIndex(refX, refY)
      sum += _rgbBuf[idx+colorBufOfs]
      num++
    }
    return sum / num
  }

  function _calcAverageR(x, y) {
    var refPositions1 = [[1, 0], [-1, 0]]
    var refPositions2 = [[0, -1], [0, 1]]
    var refPositions3 = [[1, -1], [-1, -1], [-1, 1], [1, 1]]
    if (y % 2 == 0)
      return _calcAverageColor(x, y, 0, refPositions1)
    if (x % 2 == 0)
      return _calcAverageColor(x, y, 0, refPositions2)
    return _calcAverageColor(x, y, 0, refPositions3)
  }

  function _calcAverageG(x, y) {
    var refPositions = [[1, 0], [0, -1], [-1, 0], [0, 1]]
    return _calcAverageColor(x, y, 1, refPositions)
  }

  function _calcAverageB(x, y) {
    var refPositions1 = [[1, 0], [-1, 0]]
    var refPositions2 = [[0, -1], [0, 1]]
    var refPositions3 = [[1, -1], [-1, -1], [-1, 1], [1, 1]]
    if (y % 2 == 1)
      return _calcAverageColor(x, y, 2, refPositions1)
    if (x % 2 == 1)
      return _calcAverageColor(x, y, 2, refPositions2)
    return _calcAverageColor(x, y, 2, refPositions3)
  }

  function makeRgbBuf() {
    if (!_rawBuf)
      makeRawBuf()
    var size = _idf.width * _idf.height
    _rgbBuf = new Uint16Array(size*3)

    //
    // Fill basic points
    //
    for (var y = 0; y < _idf.height; y++) {
      for (var x = 0; x < _idf.width; x++) {
        var idx = _calcRgbBufIndex(x, y)
        var v = _rawDataAt(x, y)
        var r = 0
        var g = 0
        var b = 0
        if (y % 2 == 0) {
          if (x % 2 == 0) { // R
            r = v
          } else {          // G
            g = v
          }
        } else {
          if (x % 2 == 0) { // G
            g = v
          } else {          // B
            b = v
          }
        }
        _rgbBuf[idx+0] = r
        _rgbBuf[idx+1] = g
        _rgbBuf[idx+2] = b
      }
    }

    //
    // interpolation
    //
    for (var y = 0; y < _idf.height; y++) {
      for (var x = 0; x < _idf.width; x++) {
        var idx = _calcRgbBufIndex(x, y)
        var n_sum = 0
        if (y % 2 == 0) {
          if (x % 2 == 0) { // R
            _rgbBuf[idx+1] = _calcAverageG(x, y)
            _rgbBuf[idx+2] = _calcAverageB(x, y)
          } else {          // G
            _rgbBuf[idx+0] = _calcAverageR(x, y)
            _rgbBuf[idx+2] = _calcAverageB(x, y)
          }
        } else {
          if (x % 2 == 0)  { // G
            _rgbBuf[idx+0] = _calcAverageR(x, y)
            _rgbBuf[idx+2] = _calcAverageB(x, y)
          } else {           // B
            _rgbBuf[idx+0] = _calcAverageR(x, y)
            _rgbBuf[idx+1] = _calcAverageG(x, y)
          }
        }
      }
    }
  }

  //
  // public methods
  //
  this.rawDataAt = function (x, y) {
    return _rawDataAt(x,y)
  }

  this.rawData8bitAt = function(x, y) {
    var v = this.rawDataAt(x, y)
    if (_idf.bps > 8) {
      var shift = _idf.bps - 8
      v = v >> shift
    }
    return v
  }

  this.processRGBData = function(camToRgb, NormVec) {
    makeRgbBuf()
    var maxVal = (1 << _idf.bps) - 1;
    var gamma = 2.2
    var invGamma = 1/gamma
    var camData = [[0], [0], [0]]
    for (var y = 0; y < _idf.height; y++) {
      for (var x = 0; x < _idf.width; x++) {
        var idx = (y * _idf.width + x) * 3
        for (var i = 0; i < 3; i++) {
          var v = _rgbBuf[idx+i] * NormVec[i]
          if (v < 0)
            v = 0
          else if (v > maxVal)
            v = maxVal
          // Convert to sRGB
          camData[i][0] = Math.pow(v/maxVal, invGamma) * maxVal;
        }
        var val = matrixMul(camToRgb, camData)
        for (var i = 0; i < 3; i++)
          _rgbBuf[idx+i] = ~~(val[i])
      }
    }
  }

  this.rgbDataAt = function (x, y) {
    var idx = (y * _idf.width + x) * 3
    return [_rgbBuf[idx], _rgbBuf[idx+1], _rgbBuf[idx+2]]
  }

  this.rgbData8bitAt = function(x, y) {
    var rgb = this.rgbDataAt(x, y)
    if (_idf.bps > 8) {
      var shift = _idf.bps - 8
      rgb[0] = rgb[0] >> shift
      rgb[1] = rgb[1] >> shift
      rgb[2] = rgb[2] >> shift
    }
    return rgb
  }
}

function matrixMul(a, b) {
  numLinesA = a.length
  numColumnsA = a[0].length
  numLinesB = b.length
  numColumnsB = b[0].length
  if (numLinesA != numLinesB) {
    throw new Error("Cannot multiply A("
                    + numLinesA + "x" + numColumnsA + ") and B("
                    + numLinesB + "x" + numColumnsB + ")")
  }

  var m = numLinesA
  var n = numColumnsB
  var l = numColumnsA
  var matrix = [m]
  for (var i = 0; i < m; i++) {
    matrix[i] = [n]
    for (var j = 0; j < n; j++) {
      matrix[i][j] = 0
      for (var k = 0; k < l; k++)
        matrix[i][j] += a[i][k] * b[k][j]
    }
  }
  return matrix
}

function matrixDet(a) {
  var m = a.length
  var n = a[0].length
  var v = 0

  if (m == 3 || n == 3) {
    v = a[0][0] * a[1][1] * a[2][2]
      + a[1][0] * a[2][1] * a[0][2]
      + a[2][0] * a[0][1] * a[1][2]
      - a[0][2] * a[1][1] * a[2][0]
      - a[0][1] * a[1][0] * a[2][2]
      - a[0][0] * a[1][2] * a[2][1]
  }
  else {
    throw new Error("Not implement: Det.: " + m + "x" + n)
  }
  return v
}

function matrixInv(a) {
  var m = a.length
  var n = a[0].length
  if (m != 3 || n != 3) {
    throw new Error("Not implement: Inverset: " + m + "x" + n)
  }

  var det = matrixDet(a)
  var matrix = [[a[1][1]*a[2][2] - a[1][2]*a[2][1],
                 a[0][2]*a[2][1] - a[0][1]*a[2][2],
                 a[0][1]*a[1][2] - a[0][2]*a[1][1]],
                [a[1][2]*a[2][0] - a[1][0]*a[2][2],
                 a[0][0]*a[2][2] - a[0][2]*a[2][0],
                 a[0][2]*a[1][0] - a[0][0]*a[1][2]],
                [a[1][0]*a[2][1] - a[1][1]*a[2][0],
                 a[0][1]*a[2][0] - a[0][0]*a[2][1],
                 a[0][0]*a[1][1] - a[0][1]*a[1][0]]]
  for (var i = 0; i < m; i++) {
    for (var j = 0; j < n; j++)
      matrix[i][j] /= det
  }
  return matrix
}

var DNGParser = function(data) {

  //
  // constant
  //
  var UNCOMPRESSED = 1
  var COMPRESSED = 7

  //
  // private members
  //
  var _index = 0
  var _buf = new Uint8Array(data)
  var _bigEndian = false
  var _colorPlanes = 0
  var _cfaPattern = undefined
  var _cfaPatternSize = undefined
  var _idfArray = []
  var _currIdf = undefined
  var _colorMatrix1 = undefined;
  var _colorMatrix2 = undefined;
  var _RGBtoXYZMatrix = [[0.412453, 0.357580, 0.180423],
                         [0.212671, 0.715160, 0.072169],
                         [0.019334, 0.119193, 0.950227]];
  var _asShotNeutral = []
  var _RGB_to_CameraMatrix = undefined
  var _RGB_NormalizeVector = undefined
  var _Camera_to_RGBMatrix = undefined

  var _parseTagFuncs = {
    256:_parseTagWidth, 257:_parseTagHeight, 258:_parseTagBitsPerSample,
    259:_parseTagCompression, 262:_parseTagPhotometricInterpretation,
    270:_parseTagImageDescrption, 271:_parseTagMaker, 272:_parseTagModel,
    273:_parseTagStripOffset, 274:_parseTagOrientation,
    277:_parseTagSamplesPerPixel, 279:_parseTagImageSize,
    306:_parseTagDateTime,
    330:_parseTagSubIFDs,
    33422:_parseTagCFAPattern,
    34665:_parseTagExif,
    50706:_parseTagDNGVersion,
    50710:_parseTagCFAPlaneColor, 50711:_parseTagCFALayout,
    50713:_parseTagBlackLevelRepeatDim, 50714:_parseTagBlackLevel,
    50717:_parseTagWhiteLevel,
    50718:_parseTagDefaultScale,
    50721:_parseTagColorMatrix1, 50722:_parseTagColorMatrix2,
    50723:_parseTagCameraCalibration1, 50724:_parseTagCameraCalibration1,
    50727:_parseTagAnalogBalance, 50728:_parseTagAsShotNeutral,
    50778:_parseTagCalibrationIlluminant1,
    50779:_parseTagCalibrationIlluminant2,
  }

  var _parseExifFuncs = {
    33434:_parseExifShutter, 33437:_parseExifAperture,
    34855:_parseExifISOSpeed,
    36867:_parseExifDateTime, 36868:_parseExifDateTime,
    37378:_parseExifAperturePow2,
    37386:_parseExifFocalLength,
  }

  //
  // private methods
  //
  function _getStreamData(bytes) {
    var sum = 0
    for (var i = 0; i < bytes; i++, _index++) {
      var v = _buf[_index]
      if (_bigEndian == true)
        sum = sum << 8
      else
        v = v << (8 * i)
      sum += v
    }
    return sum
  }

  function _getStreamDataSigned(bytes) {
    var v = _getStreamData(bytes)
    var mask = 1 << ((8 * bytes) - 1)
    if ((v & mask) == 0)
      return v; // positive value
    var v2c = -((mask << 1) - v)
    return v2c
  }


  function _getStreamDataAsUint8Array(bytes) {
    var sub = _buf.subarray(_index, _index + bytes)
    return sub
  }

  function _getStreamDataInt(type) {
    if (type == 3)
        return _getStreamData(2)
    else
        return _getStreamData(4)
  }

  function _getStreamAsString(bytes) {
    var str = ""
    for (var i = 0; i < bytes; i++, _index++) {
      var code = _buf[_index]
      if (code == 0)
        break
      str += String.fromCharCode(code)
    }
    return str
  }

  function _getStreamDataReal(type) {
    switch (type) {
     case 3:
      return _getStreamData(2)
     case 4:
      return _getStreamData(4)
     case 5:
      var v1 = _getStreamData(4)
      var v2 = _getStreamData(4)
      return v1/v2
     case 8:
      return _getStreamDataSigned(2)
     case 9:
      return _getStreamDataSigned(4)
     case 10:
      var v1 = _getStreamDataSigned(4)
      var v2 = _getStreamDataSigned(4)
      return v1/v2
     case 11:
      throw new Error("Not implement: int to float")
     case 12:
      throw new Error("Not implement: type 12")
     default:
      return _getStreamData(1)
    }
  }

  function _skipStreamData(bytes) {
    _index += bytes
  }

  function _setStreamPosition(index) {
    _index = index
  }

  function _getStreamPosition() {
    return _index
  }

  function _parseEndian() {
    var v1 = _getStreamData(1)
    var v2 = _getStreamData(1)
    if (v1 == 0x49 && v2 == 0x49)      // II: LE
      _bigEndian = false
    else if (v1 == 0x4d && v2 == 0x4d) // MM: BE
      _bigEndian = true
    else
      throw new Error("Parse: Endian")

    // skip next 2bytes
    _skipStreamData(2)
  }

  function _parseTagWidth(type, size) {
    _currIdf.width = _getStreamDataInt(type)
    console.log("width: " + _currIdf.width)
  }

  function _parseTagHeight(type, size) {
    _currIdf.height = _getStreamDataInt(type)
    console.log("height: " + _currIdf.height)
  }

  function _parseTagBitsPerSample(type, size) {
    _currIdf.bps = _getStreamDataInt(type)
    _currIdf.samples = size & 7
    console.log("Samples: " + _currIdf.samples
                + ", Bits/Sampel: " + _currIdf.bps)
  }

  function _parseTagCompression(type, size) {
    _currIdf.compression = _getStreamDataInt(type)
    console.log("Compression: " + _currIdf.compression)
  }

  function _parseTagPhotometricInterpretation(type, size) {
    _currIdf.photoInterpret = _getStreamData(2)
    console.log("PhotometricInterpretation: " + _currIdf.photoInterpret)
  }

  function _parseTagImageDescrption(type, size) {
    var imageDescr = _getStreamAsString(512)
    console.log("Image Description: " + imageDescr)
  }

  function _parseTagMaker(type, size) {
    var maker = _getStreamAsString(64)
    console.log("Maker: " + maker)
  }

  function _parseTagModel(type, size) {
    var model = _getStreamAsString(64)
    console.log("Model: " + model)
  }

  function _parseTagStripOffset(type, size) {
    _currIdf.stripOffset = _getStreamData(4)
    console.log("Strip Offset: " + _currIdf.stripOffset)
  }

  function _parseTagOrientation(type, size) {
    var table = [5, 0, 1, 3, 2, 4, 6, 7]
    var idx = _getStreamData(2) & 7
    var orientation = table[idx]
    console.log("Orientation: " + orientation)
  }

  function _parseTagSamplesPerPixel(type, size) {
    var samplesPerPixel = _getStreamDataInt() & 7
    console.log("samplesPerPixel: " + samplesPerPixel)
  }

  function _parseTagImageSize(type, size) {
    _currIdf.imageSize = _getStreamData(4)
    console.log("imageSize: " + _currIdf.imageSize)
  }

  function _parseTagDateTime(type, size) {
    var dateTime = _getStreamAsString(19)
    console.log("DateTime: " + dateTime)
  }

  function _parseTagSubIFDs(type, size) {
    var positions = []
    for (var i = 0; i < size; i++)
      positions[i] = _getStreamData(4)
    for (var i = 0; i < size; i++) {
      console.log("=============== SubIDFs ================")
      _setStreamPosition(positions[i])
      _parseTiffIfd()
      console.log("========================================")
    }
  }

  function _parseTagCFAPattern(type, size) {
    _cfaPatternSize = size
    if (_cfaPatternSize > 16)
      _cfaPatternSize = 16
    _cfaPattern = _getStreamDataAsUint8Array(_cfaPatternSize)
    var colors = 0
    var cfa = 0
    for (var i = 0; i < _cfaPatternSize; i++) {
      console.log("cfaPat[" + i + "] " + _cfaPattern[i])
      if ((cfa & (1 << _cfaPattern[i])) == 0)
        colors++ 
      cfa |= 1 << _cfaPattern[i]
    }
    console.log("CFA: " + cfa + ", colors: " + colors + ": guss_cfa_pc nedded?")
  }

  function _parseTagExif(type, size) {
    var offset = _getStreamData(4)
    _setStreamPosition(offset)
    _parseExif()
  }

  function _parseTagDNGVersion(type, size) {
    var dngVersion = []
    for (var i = 0; i < 4; i++)
      dngVersion[i] = _getStreamData(1)

    var verString = ""
    for (var i = 0; i < 4; i++) {
      verString += dngVersion[i]
      if (i != 3)
        verString += "."
    }
    console.log("DNG version: " + verString)
  }

  function _parseTagCFAPlaneColor(type, size) {
    _colorPlanes = size
    if (_colorPlanes > 4)
      _colorPlanes = 4
    var cfaPC = _getStreamDataAsUint8Array(_colorPlanes)
    var tab = []
    for (var i = 0; i < _colorPlanes; i++)
      tab[cfaPC[i]] = i
    var filters = 0
    for (var i = 16; i > 0; i--)
      filters = (filters << 2) | tab[_cfaPattern[i % _cfaPatternSize]];

    console.log("filters: " + filters.toString(16))
  }

  function _parseTagCFALayout(type, size) {
    var cfaLayout = _getStreamData(2)
    console.log("cfaLayout: " + cfaLayout)
  }

  function _parseTagBlackLevelRepeatDim(type, size) {
    var blrr = _getStreamData(2)
    var blrc = _getStreamData(2)
    console.log("blrr: " + blrr + ", blrc: " + blrc)
  }

  function _parseTagBlackLevel(type, size) {
    var blackLevel = _getStreamDataReal(type)
    console.log("blackLevel: " + blackLevel + ", Should add code to repeat !!")
  }

  function _parseTagWhiteLevel(type, size) {
    var whiteLevel = _getStreamDataInt(type)
    console.log("whiteLevel: " + whiteLevel)
  }

  function _parseTagDefaultScale(type, size) {
    var v1 = _getStreamDataReal(type)
    var v2 = _getStreamDataReal(type)
    var defaultScale = v1 / v2
    console.log("Default Scale:  " + defaultScale)
  }

  function _parseTagColorMatrix(type, size) {
    var matrix = []
    for (var i = 0; i < _colorPlanes; i++) {
      matrix[i] = []
      for (var j = 0; j < 3; j++)
        matrix[i][j] = _getStreamDataReal(type)
    }
    return matrix
  }

  function _parseTagColorMatrix1(type, size) {
    _colorMatrix1 = _parseTagColorMatrix(type, size)
    console.log("ColorMatrix1: " + _colorMatrix1)
  }

  function _parseTagColorMatrix2(type, size) {
    _colorMatrix2 = _parseTagColorMatrix(type, size)
    console.log("ColorMatrix2: " + _colorMatrix2)
  }

  function _parseTagCameraCalibration(type, size) {
    var matrix = []
    for (var i = 0; i < _colorPlanes; i++) {
      matrix[i] = []
      for (var j = 0; j < 3; j++)
        matrix[i][j] = _getStreamDataReal(type)
      for (var j = 0; j < 3; j++)
        console.log("ColorMatrix[" + i + "][" + j + "] " + matrix[i][j])
    }
  }

  function _parseTagCameraCalibration1(type, size) {
    console.log("CameraCalibration1")
    _parseTagCameraCalibration(type, size)
  }

  function _parseTagCameraCalibration2(type, size) {
    console.log("CameraCalibration2")
    _parseTagCameraCalibration(type, size)
  }

  function _parseTagAnalogBalance(type, size) {
    var analogBalance = []
    for (var i = 0; i < _colorPlanes; i++)
      analogBalance[i] = _getStreamDataReal(type)
    for (var i = 0; i < _colorPlanes; i++)
      console.log("AnalogBalance[" + i + "]: " + analogBalance[i])
  }

  function _parseTagAsShotNeutral(type, size) {
    for (var i = 0; i < _colorPlanes; i++)
      _asShotNeutral[i] = _getStreamDataReal(type)
    for (var i = 0; i < _colorPlanes; i++)
      console.log("AsShotNeutral[" + i + "]: " + _asShotNeutral[i])
  }

  function _parseTagCalibrationIlluminant1(type, size) {
    var calib = _getStreamData(2)
    console.log("CalibrationIlluminat1: " + calib)
  }

  function _parseTagCalibrationIlluminant2(type, size) {
    var calib = _getStreamData(2)
    console.log("CalibrationIlluminat1: " + calib)
  }

  function _parseTagSetStreamPosition(type, size) {
    var table = [1, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8, 8]
    var v = table[0]
    if (type < 14)
        v = table[type]
    if (size * v > 4) {
      var pos = _getStreamData(4)
      _setStreamPosition(pos)
    }
  }

  function _parseTagHeaderAndSetStreamPosition() {
    var header = new TagHeader()
    header.tag = _getStreamData(2)
    header.type = _getStreamData(2)
    header.size = _getStreamData(4)
    header.nextPosition = _getStreamPosition() + 4
    _parseTagSetStreamPosition(header.type, header.size)
    return header
  }

  function _parseTag() {
    var header  = _parseTagHeaderAndSetStreamPosition()
    var func = _parseTagFuncs[header.tag]
    if (func != undefined)
      func(header.type, header.size)
    else
      console.log("Undefined parser: Tag/type/size: "
                  + header.tag + "/" + header.type + "/" + header.size)
    _setStreamPosition(header.nextPosition)
  }

  function _parseTiffIfd() {
    var entries = _getStreamData(2)
    console.log("entries: " + entries)
    var saveIdf = _currIdf
    _currIdf = new IdfData()
    _idfArray.push(_currIdf)
    for (var i = 0; i < entries; i++)
      _parseTag()
    _currIdf = saveIdf
  }

  //
  // Exif parsers
  //
  function _parseExifShutter(type) {
    var shutter = _getStreamDataReal(type)
    console.log("Shutter: " + shutter)
  }

  function _parseExifAperture(type) {
    var aperture = _getStreamDataReal(type)
    console.log("Aperture: " + aperture)
  }

  function _parseExifISOSpeed(type) {
    var isoSpeed = _getStreamData(2)
    console.log("ISO Speed: " + isoSpeed)
  }

  function _parseExifDateTime(type) {
    var dateTime = _getStreamAsString(19)
    console.log("DateTime: " + dateTime)
  }

  function _parseExifAperturePow2(type) {
    var aperture = Math.pow(2, _getStreamDataReal(type)/2)
    console.log("Aperture: " + aperture)
  }

  function _parseExifFocalLength(type) {
    var focalLength = _getStreamDataReal(type)
    console.log("Focal Length: " + focalLength)
  }

  function _parseExifEach() {
    var header  = _parseTagHeaderAndSetStreamPosition()
    var func = _parseExifFuncs[header.tag]
    if (func != undefined)
      func(header.type)
    else
      console.log("Undefined Exif parser: Tag/type/size: "
                  + header.tag + "/" + header.type + "/" + header.size)
    _setStreamPosition(header.nextPosition)
  }

  function _parseExif() {
    var entries = _getStreamData(2)
    for (var i = 0; i < entries; i++)
      _parseExifEach()
  }

  //
  // Raw data paserser
  //
  function _normalizeRGBCameraMatrix() {
    _RGB_NormalizeVector = []
    for (var i = 0; i < 3; i++) {
      var sum = 0;
      for (var j = 0; j < 3; j++)
        sum += _RGB_to_CameraMatrix[i][j]
      for (var j = 0; j < 3; j++) {
        _RGB_to_CameraMatrix[i][j] /= sum;
        _RGB_NormalizeVector[i] = 1 / sum;
      }
    }
  }

  function _processRawIdf(idf) {
    console.log("stripOffset: " + idf.stripOffset)
    console.log("imageSize: " + idf.imageSize)
    //_RGB_to_CameraMatrix = matrixMul(_colorMatrix1, _RGBtoXYZMatrix)
    _RGB_to_CameraMatrix = matrixMul(_colorMatrix2, _RGBtoXYZMatrix)
    _normalizeRGBCameraMatrix()
    _Camera_to_RGBMatrix = matrixInv(_RGB_to_CameraMatrix)

    var rawDecoder = new RawDataDecoder(_buf.subarray(idf.stripOffset), idf)
    rawDecoder.processRGBData(_Camera_to_RGBMatrix, _RGB_NormalizeVector)
    return rawDecoder
  }

  function _draw1x1(idf, rawDecoder) {
    var canvas = document.getElementById('canvas')
    var context = canvas.getContext('2d')
    var picWidth = canvas.width
    var picHeight = canvas.height
    var img01 = context.createImageData(picWidth, picHeight)
    var ptr = img01.data
    for (var y = 0; y < picHeight; y++) {
      for (var x = 0; x < picWidth; x++) {
        var idx = (y * picWidth + x) * 4
        var val = rawDecoder.rgbData8bitAt(x, y)
        for (var i = 0; i < 3; i++)
          ptr[idx+i] = val[i]
        ptr[idx+3] = 0xff
      }
    }
    context.putImageData(img01, 0, 0)
  }

  function _drawOverall(idf, rawDecoder) {
    var canvas = document.getElementById('canvas2')
    var context = canvas.getContext('2d')
    var picRatio = idf.width / idf.height
    var canvasRatio = canvas.width / canvas.height
    if (canvasRatio != picRatio)
      canvas.height = canvas.width / picRatio
    console.log("drawOverall: " + canvas.width + "," + canvas.height)
    var widthRatio = idf.width / canvas.width
    var heightRatio = idf.height / canvas.height

    var img = context.createImageData(canvas.width, canvas.height)
    var ptr = img.data
    for (var y = 0; y < canvas.height; y++) {
      for (var x = 0; x < canvas.width; x++) {
        var x_org = ~~(x * widthRatio)
        var y_org = ~~(y * heightRatio)
        var idx = (y * canvas.width + x) * 4
        var rgb = rawDecoder.rgbData8bitAt(x_org, y_org)
        for (var i = 0; i < 3; i++)
          ptr[idx+i] = rgb[i]
        ptr[idx+3] = 0xff
      }
    }
    context.putImageData(img, 0, 0)
  }

  function _processRawData() {
    console.log("parseRaw")
    var markedSize = -1
    var markedIdf = undefined
    for (var i = 0; i < _idfArray.length; i++) {
        var idf = _idfArray[i]
        if (idf.imageSize > markedSize) {
          markedSize = idf.imageSize
          markedIdf = idf
        }
    }
    var rawDecoder = _processRawIdf(markedIdf)
    _draw1x1(markedIdf, rawDecoder)
    _drawOverall(markedIdf, rawDecoder)
  }

  //
  // public methods
  //
  this.start = function() {
    _parseEndian()
    var offset = _getStreamData(4)
    _setStreamPosition(offset)
    _parseTiffIfd()
    _processRawData()
  }
}

function loadFileOnLoad(evt)
{
  var dateObj = new Date()
  var startTime = dateObj.getTime()

  var reader = evt.target
  var data = reader.result;
  var parser = new DNGParser(data)
  parser.start();

  var dateObj2 = new Date()
  var endTime = dateObj2.getTime()
  var msg = document.getElementById("msg")
  console.log("start: " + startTime + ", end: " + endTime)
  msg.innerHTML = "Process Time [s]: " + ((endTime - startTime)/1000).toFixed(3)
}

function loadFileOnError(evt)
{
  var msg = ""
  var reader = evt.target
  switch (reader.error.code) {
   case reader.error.NOT_FOUND_ERR:
    msg = "Not found Error"
    break;
   case reader.error.NOT_READABLE_ERR:
    msg = "Not readable Error"
    break;
   case reader.error.SECURITY_ERR:
    msg = "Security Error. If you use Google Chrome and " + 
          "URI with file://, you need to add the command line options " +
          "--allow-file-access-from-files --allow-file-access " +
          "to the chrome."
    break;
   default:
    msg = "Unknown Error: " + reader.error.code
  }
  alert(msg)
}

function checkExtension(fileName)
{
  var supportedExt = new Array("DNG")

  var pos = fileName.lastIndexOf(".");
  if (pos == -1)
    return false

  var fileExt = fileName.substring(pos+1).toUpperCase()
  for (var i = 0; i < supportedExt.length; i++) {
    if (fileExt == supportedExt[i])
      return true
  }
  return false
}

function loadFile(fileObj)
{
  if (!fileObj.files.length)
    return

  var file = fileObj.files[0]
  if (checkExtension(file.name) == false) {
    alert("No supported file.")
    return
  }

  var reader = new FileReader()
  reader.onload = loadFileOnLoad
  reader.onerror = loadFileOnError
  reader.readAsArrayBuffer(file)
}
// -->
</script>
</head>
<body>
<input type="file" onchange="loadFile(this)">
<br>
<canvas id="canvas" width=640 height=480>
</canvas>
<canvas id="canvas2" width=600 height=400>
</canvas>
<p id="msg">Message Region</p>
</body>
</html>
